﻿using AutoMapper;
using Mes.Application.Common;
using Mes.Application.Common.Resource;
using Mes.Application.DTO.Inputs.WorkOrders;
using Mes.Application.DTO.Results.WorkOrders;
using Mes.Domain.BoundedContext;
using Mes.Domain.BoundedContext.Entities.WorkOrders;
using Mes.Infrastructure.Data.BoundedContext.Configurations;
using Mes.Infrastructure.Data.BoundedContext.Extensions;
using Mes.Infrastructure.Data.BoundedContext.Repositories.MasterData;
using Mes.Infrastructure.Data.BoundedContext.Repositories.WorkOrders;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace Mes.Application.Services.WorkOrders
{
    public class JobService : IJobService
    {
        private readonly IMapper _mapper;
        private readonly IWoRepository _woRepository;
        private readonly IJobRepository _jobRepository;
        private readonly IJobRouteRepository _jobRouteRepository;
        private readonly IProductionUnitRepository _productionUnitRepository;
        private readonly IMaterialRepository _materialRepository;
        private readonly IJobStepGrpRepository _jobStepGrpRepository;
        private readonly IJobStateRepository _job_StateRepository;
        private readonly IJobBOMRepository _jobBOMRepository;
        private readonly IJobStepRepository _jobStepRepository;
        private readonly BuiltInTypesConfiguration _builtInTypes;
        private readonly IJobBomStepRepository _jobBomStepRepository;

        public JobService(IMapper mapper, IWoRepository woRepository, IJobRepository jobRepository, IJobRouteRepository jobRouteRepository, IProductionUnitRepository productionUnitRepository,
            IMaterialRepository materialRepository, IJobStepGrpRepository jobStepGrpRepository, IJobStateRepository job_StateRepository, IJobBOMRepository jobBOMRepository, IJobStepRepository jobStepRepository, IOptions<BuiltInTypesConfiguration> options, IJobBomStepRepository jobBomStepRepository)
        {
            if (mapper == null || woRepository == null || jobRepository == null || jobRouteRepository == null || productionUnitRepository == null || materialRepository == null
                || jobStepGrpRepository == null || job_StateRepository == null || jobBOMRepository == null || jobStepRepository == null|| jobBomStepRepository==null)
            {
                throw new ArgumentNullException();
            }
            _mapper = mapper;
            _woRepository = woRepository;
            _jobRepository = jobRepository;
            _jobRouteRepository = jobRouteRepository;
            _productionUnitRepository = productionUnitRepository;
            _materialRepository = materialRepository;
            _jobStepGrpRepository = jobStepGrpRepository;
            _job_StateRepository = job_StateRepository;
            _jobBOMRepository = jobBOMRepository;
            _jobStepRepository = jobStepRepository;
            _builtInTypes = options.Value;
            _jobBomStepRepository = jobBomStepRepository;
        }

        public async Task AddJobAsync(AddingJobInput input)
        {
            var wo = await _woRepository.FindAsync(x => x.Wo_Id == input.Wo_Id);
            if (wo == null)
            {
                throw new CustomException(Messages.WoNotFound);
            }
            if (await _jobRepository.ExistsAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id))
            {
                throw new CustomException(Messages.JobExist);
            }
            if (!await _productionUnitRepository.ExistsAsync(x => x.Id == input.Init_Sched_Ent_Id))
            {
                throw new CustomException(Messages.ProductionUnitNotFound);
            }
            if (!await _productionUnitRepository.ExistsAsync(x => x.Id == input.Target_Sched_Ent_Id))
            {
                throw new CustomException(Messages.ProductionUnitNotFound);
            }
            var material = await _materialRepository.FindAsync(x => x.Id == input.Item_Id);
            if (material == null)
            {
                throw new CustomException(Messages.MaterialNotFound);
            }
            if (material.MaterialClassTypeName != _builtInTypes.BuiltInMaterialClassTypes[0])
            {
                throw new CustomException(Messages.MaterialClassTypeNotProduction);
            }
            //所需产量不能大于开工数量
            if (input.Qty_Reqd > input.Qty_At_Start)
            {
                throw new CustomException(Messages.StartQtyLessReqQty);
            }
            //工单的开工数量等于所有job的开工数量的合计
            //工单的所需产量等于所有job的所需产量的合计
            input.Qty_At_Start = Math.Round(input.Qty_At_Start, material.PrecisionPlace, MidpointRounding.AwayFromZero);
            input.Qty_Reqd = Math.Round(input.Qty_Reqd, material.PrecisionPlace, MidpointRounding.AwayFromZero);
            var allOriginJobs = await _jobRepository.Query().Where(x => x.Wo_Id == input.Wo_Id).ToListAsync();
            if (allOriginJobs.Count > 0)
            {
                wo.Start_Qty = allOriginJobs.Sum(x => x.Qty_At_Start) + input.Qty_At_Start;
                wo.Req_Qty = allOriginJobs.Sum(x => x.Qty_Reqd) + input.Qty_Reqd;
            }
            else
            {
                wo.Start_Qty = input.Qty_At_Start;
                wo.Req_Qty = input.Qty_Reqd;
            }

            var job = _mapper.Map<Job>(input);
            var jobs = await _jobRepository.Query().Where(x => x.Wo_Id == input.Wo_Id).ToListAsync();
            if (jobs.Count == 0)
            {
                job.First_Job = true;
                job.Final_Job = true;
                job.Display_Seq = 1;
            }
            else
            {
                var finalJob = jobs.FirstOrDefault(x => x.Final_Job == true);
                if (finalJob == null)
                {
                    throw new CustomException(Messages.JobDataException);
                }
                finalJob.Final_Job = false;
                job.Final_Job = true;
                job.Display_Seq = finalJob.Display_Seq + 1;
                var route = new JobRoute()
                {
                    Input_Oper_Id = finalJob.Oper_Id,
                    Oper_Id = job.Oper_Id,
                    Wo_Id = input.Wo_Id,
                };
                await _jobRouteRepository.AddAsync(route);
            }
            job.State_Cd = 1;//创建job时默认的状态是NEW
            await _jobRepository.AddAsync(job);

            //作业物料清单
            //JOBBOM必须传
            if (input.JobBOMs.Count <= 0)
            {
                throw new CustomException(Messages.JobBomRequired);
            }
            //不能重复
            if (input.JobBOMs.Count != input.JobBOMs.Distinct().Count())
            {
                throw new CustomException(Messages.MaterialDuplication);
            }
            //第一条必须是成品物料，和作业的成品物料相同
            var finishedMaterial = input.JobBOMs[0];
            if (finishedMaterial.MaterialId != input.Item_Id)
            {
                throw new CustomException(Messages.JobBomFirstItemMustFinished);
            }
            //剩下的必须是消耗品物料
            var materialIds = input.JobBOMs.Where(x => x.MaterialId != input.Item_Id).Select(x => x.MaterialId).ToList();
            var materials = await _materialRepository.Query().Where(x => materialIds.Contains(x.Id)).ToListAsync();
            if (materials.Count(x => x.MaterialClassTypeName == _builtInTypes.BuiltInMaterialClassTypes[1]) != materials.Count)
            {
                throw new CustomException(Messages.MaterialClassTypeNotConsumables);
            }
            var jobBOMs = new HashSet<JobBom>();
            var bomValidators = new List<StepBomValidator>();
            for (int i = 0; i < input.JobBOMs.Count; i++)
            {
                //if (item.Quantity > item.MaxConsumeQuantity || item.Quantity < item.MinConsumeQuantity || item.MinConsumeQuantity > item.MaxConsumeQuantity)
                //{
                //    throw new CustomException(Messages.InputQuantityIlleagl);
                //}
                var bomItem = new JobBom(input.Wo_Id, input.Oper_Id, i, input.JobBOMs[i].MaterialId, input.JobBOMs[i].DefaultStorageEntity, input.JobBOMs[i].Quantity, /*item.MaxConsumeQuantity, item.MinConsumeQuantity,*/ input.JobBOMs[i].StartReqQuantity);
                jobBOMs.Add(bomItem);
                if (i > 0)
                {
                    bomValidators.Add(new StepBomValidator() { Bom_Pos=i,Quantity= input.JobBOMs[i].Quantity });
                }
            }
            await _jobBOMRepository.AddRangeAsync(jobBOMs);

            //判断工步BOM消耗量不能大于作业BOM数量
            foreach (var item in input.JobStepGrps)
            {
                foreach (var step in item.JobSteps)
                {
                    for (int i =0; i <step.JobBomSteps.Count; i++)
                    {
                        bomValidators[i].AllConsume = bomValidators[i].AllConsume + step.JobBomSteps[i].Qty_Per_Parent_Item;
                        if (bomValidators[i].AllConsume > bomValidators[i].Quantity)
                        {
                            throw new CustomException(Messages.StepBomConsumedQuantityLessThenJobBomQuantity);
                        }
                    }
                }
            }

            //工步组
            if (input.JobStepGrps.Count > 0)
            {
                Dictionary<int, int> stepGrpIds = new Dictionary<int, int>();
                var jobStepGrps = new HashSet<JobStepGrp>();
                foreach (var item in input.JobStepGrps)
                {
                    if (_jobStepGrpRepository.Exists(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id && x.Step_Grp_Id == item.Step_Grp_Id))
                    {
                        throw new CustomException(Messages.StepGroupAlreadyExist);
                    }

                    if (stepGrpIds.ContainsKey(item.Step_Grp_Id))
                    {
                        throw new CustomException(Messages.StepGroupDuplication);
                    }
                    else
                    {
                        stepGrpIds[item.Step_Grp_Id] = 1;
                    }

                    var jobStepGrp = _mapper.Map<JobStepGrp>(item);
                    jobStepGrp.Wo_Id = input.Wo_Id;
                    jobStepGrp.Oper_Id = input.Oper_Id;
                    jobStepGrps.Add(jobStepGrp);

                    //添加工步
                    if (item.JobSteps.Count > 0)
                    {
                        var jobSteps = new HashSet<JobStep>();
                        foreach (var step in item.JobSteps)
                        {
                            if (item.JobSteps.Count(x => x.Step_No == step.Step_No) > 1)
                            {
                                throw new CustomException(Messages.StepDuplication);
                            }
                            var currentStep = _mapper.Map<JobStep>(step);
                            currentStep.Wo_Id = input.Wo_Id;
                            currentStep.Oper_Id = input.Oper_Id;
                            currentStep.Step_Grp_Id = item.Step_Grp_Id;
                            jobSteps.Add(currentStep);

                            //工步BOM
                            if (step.JobBomSteps.Count > 0)
                            {
                                if (step.JobBomSteps.Count > materialIds.Count)
                                {
                                    throw new CustomException(Messages.StepBomNumberLessThenJobBomNumber);
                                }
                                var stepBoms = new List<JobBomStep>();
                                for (int k = 1; k <= step.JobBomSteps.Count; k++)
                                {
                                    var stepbom = new JobBomStep(input.Wo_Id, input.Oper_Id, k, item.Step_Grp_Id, step.Step_No, step.JobBomSteps[k-1].Qty_Per_Parent_Item);
                                    stepBoms.Add(stepbom);
                                }
                                await _jobBomStepRepository.AddRangeAsync(stepBoms);
                            }
                        }
                        await _jobStepRepository.AddRangeAsync(jobSteps);
                    }
                }
                await _jobStepGrpRepository.AddRangeAsync(jobStepGrps);
            }

            var status = await _jobRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 用于验证工步Bom的消耗量不能大于作业BOM的数量
        /// </summary>
        private class StepBomValidator
        {
            /// <summary>
            /// 位置
            /// </summary>
            public int Bom_Pos { get; set; }

            /// <summary>
            /// 数量
            /// </summary>
            public double Quantity { get; set; }

            /// <summary>
            /// 所有消耗的量
            /// </summary>
            public double AllConsume { get; set; }
        }

        public async Task UpdateJobAsync(UpdatingJobInput input)
        {
            var wo = await _woRepository.FindAsync(x => x.Wo_Id == input.Wo_Id);
            if (wo == null)
            {
                throw new CustomException(Messages.WoNotFound);
            }
            var material = await _materialRepository.FindAsync(x => x.Id == input.Item_Id);
            if (material == null)
            {
                throw new CustomException(Messages.MaterialNotFound);
            }
            //所需产量不能大于开工数量
            if (input.Qty_Reqd > input.Qty_At_Start)
            {
                throw new CustomException(Messages.StartQtyLessReqQty);
            }
            //工单的开工数量等于所有job的开工数量的合计
            //工单的所需产量等于所有job的所需产量的合计
            input.Qty_At_Start = Math.Round(input.Qty_At_Start, material.PrecisionPlace, MidpointRounding.AwayFromZero);
            input.Qty_Reqd = Math.Round(input.Qty_Reqd, material.PrecisionPlace, MidpointRounding.AwayFromZero);
            var allJobs = await _jobRepository.Query().Where(x => x.Wo_Id == input.Wo_Id).ToListAsync();
            var job = allJobs.FirstOrDefault(x => x.Oper_Id == input.Oper_Id);
            if (job == null)
            {
                throw new CustomException(Messages.WoHasNoJob);
            }
            var allOriginJobs = allJobs.Where(x => x.Oper_Id != input.Oper_Id).ToList();
            if (allOriginJobs.Count > 0)
            {
                wo.Start_Qty = allOriginJobs.Sum(x => x.Qty_At_Start) + input.Qty_At_Start;
                wo.Req_Qty = allOriginJobs.Sum(x => x.Qty_Reqd) + input.Qty_Reqd;
            }
            else
            {
                wo.Start_Qty = input.Qty_At_Start;
                wo.Req_Qty = input.Qty_Reqd;
            }

            job.Job_Desc = input.Job_Desc;
            job.Req_Finish_Time_Local = input.Req_Finish_Time_Local;
            job.Job_Priority = input.Job_Priority;
            job.Item_Id = input.Item_Id;
            //job.State_Cd = input.State_Cd;
            job.Qty_At_Start = input.Qty_At_Start;
            job.Qty_Reqd = input.Qty_Reqd;
            job.Init_Sched_Ent_Id = input.Init_Sched_Ent_Id;
            job.Target_Sched_Ent_Id = input.Target_Sched_Ent_Id;
            job.Act_Start_Time_Local = input.Act_Start_Time_Local;
            job.Act_Finish_Time_Local = input.Act_Finish_Time_Local;
            job.Notes = input.Notes;

            //作业物料清单
            //JOBBOM必须传
            if (input.JobBOMs.Count <= 0)
            {
                throw new CustomException(Messages.JobBomRequired);
            }
            //不能重复
            if (input.JobBOMs.Count != input.JobBOMs.Distinct().Count())
            {
                throw new CustomException(Messages.MaterialDuplication);
            }
            //第一条必须是成品物料，和作业的成品物料相同
            var finishedMaterial = input.JobBOMs[0];
            if (finishedMaterial.MaterialId != input.Item_Id)
            {
                throw new CustomException(Messages.JobBomFirstItemMustFinished);
            }
            //剩下的必须是消耗品物料
            var materialIds = input.JobBOMs.Where(x => x.MaterialId != input.Item_Id).Select(x => x.MaterialId).ToList();
            var materials = await _materialRepository.Query().Where(x => materialIds.Contains(x.Id)).ToListAsync();
            if (materials.Count(x => x.MaterialClassTypeName == _builtInTypes.BuiltInMaterialClassTypes[1]) != materials.Count)
            {
                throw new CustomException(Messages.MaterialClassTypeNotConsumables);
            }
            //删除旧数据
            await _jobBOMRepository.DeleteAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id);
            //添加新数据
            var jobBOMs = new HashSet<JobBom>();
            for (int i = 0; i < input.JobBOMs.Count; i++)
            {
                //if (item.Quantity > item.MaxConsumeQuantity || item.Quantity < item.MinConsumeQuantity || item.MinConsumeQuantity > item.MaxConsumeQuantity)
                //{
                //    throw new CustomException(Messages.InputQuantityIlleagl);
                //}
                var bomItem = new JobBom(input.Wo_Id, input.Oper_Id, i, input.JobBOMs[i].MaterialId, input.JobBOMs[i].DefaultStorageEntity, input.JobBOMs[i].Quantity, /*item.MaxConsumeQuantity, item.MinConsumeQuantity,*/ input.JobBOMs[i].StartReqQuantity);
                jobBOMs.Add(bomItem);
            }
            await _jobBOMRepository.AddRangeAsync(jobBOMs);

            var oldJobStepGrps = _jobStepGrpRepository.Query().Where(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id).ToListAsync();
            if (oldJobStepGrps.Result.Count > 0)
            {
                foreach (var item in oldJobStepGrps.Result)
                {
                    if (input.JobStepGrps.Count > 0)
                    {
                        var result = input.JobStepGrps.Where(x => x.Step_Grp_Id == item.Step_Grp_Id).ToList();
                        if (result.Count > 1)
                        {
                            throw new CustomException(Messages.StepGroupDuplication);
                        }
                        else if (result.Count == 0) // 若更新的工步组的id不在工步组表中，则将工步组表中的工步组删除，同时删除工步表中此工步组的工步
                        {
                            await _jobStepGrpRepository.DeleteAsync(x => x.Wo_Id == item.Wo_Id && x.Oper_Id == item.Oper_Id && x.Step_Grp_Id == item.Step_Grp_Id);
                        }
                    }
                    else // 若更新的输入中不含任何工步组，则删除此作业下的所有工步组
                    {
                        await _jobStepGrpRepository.DeleteAsync(x => x.Wo_Id == item.Wo_Id && x.Oper_Id == item.Oper_Id);
                        break;
                    }
                }
            }

            //工步组
            if (input.JobStepGrps.Count > 0)
            {
                Dictionary<int, int> stepGrpIds = new Dictionary<int, int>();
                var jobStepGrps = new HashSet<JobStepGrp>();
                foreach (var item in input.JobStepGrps)
                {
                    if (stepGrpIds.ContainsKey(item.Step_Grp_Id))
                    {
                        throw new CustomException(Messages.StepGroupDuplication);
                    }
                    else
                    {
                        stepGrpIds[item.Step_Grp_Id] = 1;
                    }

                    var jobStepGrp = _jobStepGrpRepository.Find(x => (x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id && x.Step_Grp_Id == item.Step_Grp_Id));
                    if (jobStepGrp == null) // 若输入的工步组不存在则新增工步组和工步
                    {
                        jobStepGrp = _mapper.Map<JobStepGrp>(item);
                        jobStepGrp.Wo_Id = input.Wo_Id;
                        jobStepGrp.Oper_Id = input.Oper_Id;
                        jobStepGrps.Add(jobStepGrp);
                    }
                    else
                    {
                        jobStepGrp.Step_Grp_Desc = item.Step_Grp_Desc; // 若输入的工步组存在则更新工步组
                        jobStepGrp.Step_Grp_Seq = item.Step_Grp_Seq;
                        _jobStepGrpRepository.Update(jobStepGrp);
                    }
                }
                if (jobStepGrps.Count > 0)
                {
                    await _jobStepGrpRepository.AddRangeAsync(jobStepGrps);
                }
            }

            //删除作业的所有工步
            _jobStepRepository.Delete(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id);
            //删除工步下的所有工步BOM
            _jobBomStepRepository.Delete(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id);
            //添加工步
            if (input.JobStepGrps.Count > 0)
            {
                foreach (var item in input.JobStepGrps)
                {
                    if (item.JobSteps.Count > 0)
                    {
                        var jobSteps = new HashSet<JobStep>();
                        foreach (var step in item.JobSteps)
                        {
                            if (item.JobSteps.Count(x => x.Step_No == step.Step_No) > 1)
                            {
                                throw new CustomException(Messages.StepDuplication);
                            }
                            var currentStep = _mapper.Map<JobStep>(step);
                            currentStep.Wo_Id = input.Wo_Id;
                            currentStep.Oper_Id = input.Oper_Id;
                            currentStep.Step_Grp_Id = item.Step_Grp_Id;
                            jobSteps.Add(currentStep);

                            //工步BOM
                            if (step.JobBomSteps.Count > 0)
                            {
                                if (step.JobBomSteps.Count > materialIds.Count)
                                {
                                    throw new CustomException(Messages.StepBomNumberLessThenJobBomNumber);
                                }
                                var stepBoms = new List<JobBomStep>();
                                for (int k = 1; k <= step.JobBomSteps.Count; k++)
                                {
                                    var stepbom = new JobBomStep(input.Wo_Id, input.Oper_Id, k, item.Step_Grp_Id, step.Step_No, step.JobBomSteps[k-1].Qty_Per_Parent_Item);
                                    stepBoms.Add(stepbom);
                                }
                                await _jobBomStepRepository.AddRangeAsync(stepBoms);
                            }
                        }
                        await _jobStepRepository.AddRangeAsync(jobSteps);
                    }
                }
            }

            var status = await _jobRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }
        public async Task<JobDetailsResult> GetJobAsync(GettingJobInput input)
        {
            var job = await _jobRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id);
            if (job == null)
            {
                throw new CustomException(Messages.JobNotFound);
            }
            var data = _mapper.Map<JobDetailsResult>(job);
            var material = await _materialRepository.FindAsync(x => x.Id == data.Item_Id);
            data.Item_Name = material.Name;
            var initEnt = await _productionUnitRepository.FindAsync(x => x.Id == data.Init_Sched_Ent_Id);
            data.Init_Sched_Ent_Name = initEnt?.ProductionUnitName ?? String.Empty;
            var targetEnt = await _productionUnitRepository.FindAsync(x => x.Id == data.Target_Sched_Ent_Id);
            data.Target_Sched_Ent_Name = targetEnt?.ProductionUnitName ?? String.Empty;
            if (data.Run_Ent_Id != null)
            {
                var runEnt = await _productionUnitRepository.FindAsync(x => x.Id == data.Run_Ent_Id);
                data.Run_Ent_Name = runEnt?.ProductionUnitName ?? string.Empty;
            }
            var jobState = await _job_StateRepository.FindAsync(x => x.State_Cd == data.State_Cd);
            data.State_Desc = jobState?.State_Desc ?? String.Empty;

            //作业物料清单
            var jobBOMs = await _jobBOMRepository.Query().Where(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id).OrderBy(x => x.Bom_Pos).ToListAsync();
            if (jobBOMs.Count > 0)
            {
                var bomItemList = new List<JobBOMResult>();
                foreach (var item in jobBOMs)
                {
                    var materialEntity = await _materialRepository.FindAsync(x => x.Id == item.Item_Id);
                    JobBOMResult bomItems;
                    if (item.Def_Storage_Ent_Id != null/* && item.Max_Qty_Per_Parent_Item != null && item.Min_Qty_Per_Parent_Item != null*/)
                    {
                        var productUnit = await _productionUnitRepository.FindAsync(x => x.Id == item.Def_Storage_Ent_Id);
                        bomItems = new JobBOMResult(item.Item_Id, materialEntity.Code, materialEntity.Name, item?.Def_Storage_Ent_Id, productUnit?.ProductionUnitCode, productUnit?.ProductionUnitName , item.Qty_Per_Parent_Item,
                        /*item?.Max_Qty_Per_Parent_Item, item?.Min_Qty_Per_Parent_Item,*/ item.Reqd_Start_Val,item.Bom_Pos);
                    }
                    else
                    {
                        bomItems = new JobBOMResult(item.Item_Id, materialEntity.Code, materialEntity.Name, item?.Def_Storage_Ent_Id, item.Qty_Per_Parent_Item,
                        /*item?.Max_Qty_Per_Parent_Item, item?.Min_Qty_Per_Parent_Item,*/ item.Reqd_Start_Val, item.Bom_Pos);
                    }
                    bomItemList.Add(bomItems);
                }
                data.JobBOMs = bomItemList;
            }

            //工步组
            var jobStepGrps = await _jobStepGrpRepository.Query().Where(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id).OrderBy(x => x.Step_Grp_Seq).ToListAsync();
            if (jobStepGrps.Count > 0)
            {
                data.JobStepGrps = _mapper.Map<List<JobStepGrpResult>>(jobStepGrps);
            }
            //工步
            var jobSteps = await _jobStepRepository.Query().Where(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id).ToListAsync();
            //工步Bom
            var stepBoms=await _jobBomStepRepository.Query().Where(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id).ToListAsync();
            foreach (var item in data.JobStepGrps)
            {
                var currentSteps = jobSteps.Where(x => x.Step_Grp_Id == item.Step_Grp_Id).OrderBy(x => x.Step_Seq).ToList();
                item.JobSteps = _mapper.Map<List<JobStepResult>>(currentSteps);
                //工步的Bom来源于作业的物料清单
                if (data.JobBOMs.Count > 1)
                {
                    foreach (var step in item.JobSteps)
                    {
                        var currentStepBoms = stepBoms.Where(x => x.Step_Grp_Id == item.Step_Grp_Id && x.Step_No == step.Step_No).ToList();
                        var stepBomResult = new List<JobBomStepResult>();
                        for (int i = 1; i < data.JobBOMs.Count; i++)
                        {
                            var currentStepBom = currentStepBoms.FirstOrDefault(x => x.Bom_Pos == data.JobBOMs[i].Bom_Pos);
                            //当前物料全部的消耗量
                            var allConsumQty = stepBoms.Where(x => x.Bom_Pos == data.JobBOMs[i].Bom_Pos).Sum(x => x.Qty_Per_Parent_Item);
                            //剩余数量=作业的物料数量-当前物料全部的消耗量
                            var remainingQty = data.JobBOMs[i].Quantity - allConsumQty;
                            var currentBomStepResult = new JobBomStepResult(data.JobBOMs[i].MaterialCode, data.JobBOMs[i].MaterialName, data.JobBOMs[i].Quantity, currentStepBom?.Qty_Per_Parent_Item??0, remainingQty);
                            stepBomResult.Add(currentBomStepResult);
                        }
                        step.JobBomSteps = stepBomResult;
                    }
                }
            }

            return data;
        }

        public async Task DeleteJobAsync(DeletingJobInput input)
        {
            var jobs = await _jobRepository.Query().Where(x => x.Wo_Id == input.Wo_Id).ToListAsync();
            if (jobs.Count == 0)
            {
                throw new CustomException(Messages.WoHasNoJob);
            }
            var deletejob = jobs.FirstOrDefault(x => x.Oper_Id == input.Oper_Id);
            if (deletejob == null)
            {
                throw new CustomException(Messages.WoHasNoJob);
            }
            var routes = await _jobRouteRepository.Query().Where(x => x.Wo_Id == input.Wo_Id).ToListAsync();

            if (deletejob.First_Job == true && deletejob.Final_Job == false)
            {
                var currentRoute = routes.FirstOrDefault(x => x.Input_Oper_Id == deletejob.Oper_Id);
                if (currentRoute == null)
                {
                    throw new CustomException(Messages.JobRouteDataException);
                }
                var newfirstJob = jobs.FirstOrDefault(x => x.Oper_Id == currentRoute.Oper_Id);
                if (newfirstJob == null)
                {
                    throw new CustomException(Messages.JobRouteDataException);
                }
                newfirstJob.First_Job = true;
                _jobRouteRepository.Delete(currentRoute);
                _jobRepository.Delete(deletejob);
                _jobBOMRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobStepGrpRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobStepRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobBomStepRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobRepository.Save();
            }
            else if (deletejob.First_Job == false && deletejob.Final_Job == true)
            {
                var currentRoute = routes.FirstOrDefault(x => x.Oper_Id == deletejob.Oper_Id);
                if (currentRoute == null)
                {
                    throw new CustomException(Messages.JobRouteDataException);
                }
                var newFinalJob = jobs.FirstOrDefault(x => x.Oper_Id == currentRoute.Input_Oper_Id);
                if (newFinalJob == null)
                {
                    throw new CustomException(Messages.JobRouteDataException);
                }
                newFinalJob.Final_Job = true;
                _jobRouteRepository.Delete(currentRoute);
                _jobRepository.Delete(deletejob);
                _jobBOMRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobStepGrpRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobStepRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobBomStepRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobRepository.Save();
            }
            else if (deletejob.First_Job == false && deletejob.Final_Job == false)
            {
                var firstRoute = routes.FirstOrDefault(x => x.Oper_Id == deletejob.Oper_Id);
                var secondRoute = routes.FirstOrDefault(x => x.Input_Oper_Id == deletejob.Oper_Id);
                if (firstRoute == null)
                {
                    throw new CustomException(Messages.JobRouteDataException);
                }
                if (secondRoute == null)
                {
                    throw new CustomException(Messages.JobRouteDataException);
                }
                var newRoute = new JobRoute
                {
                    Input_Oper_Id = firstRoute.Input_Oper_Id,
                    Oper_Id = secondRoute.Oper_Id,
                    Wo_Id = input.Wo_Id
                };
                _jobRouteRepository.Delete(firstRoute);
                _jobRouteRepository.Delete(secondRoute);
                _jobRepository.Delete(deletejob);
                _jobBOMRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobStepGrpRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobStepRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobBomStepRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobRepository.Save();
                _jobRouteRepository.Add(newRoute);
                _jobRouteRepository.Save();
            }
            else
            {
                _jobRepository.Delete(deletejob);
                _jobBOMRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobStepGrpRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobStepRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobBomStepRepository.Delete(x => x.Oper_Id == input.Oper_Id && x.Wo_Id == input.Wo_Id);
                _jobRepository.Save();
            }
        }

        public async Task<PagedItemsData<GettingJobQueueResult>> GetJobQueueAsync(GettingJobQueueInput input)
        {
            var queryable = from wo in _woRepository.Query()
                            join job in _jobRepository.Query()
                            on wo.Wo_Id equals job.Wo_Id
                            join ent in _productionUnitRepository.Query()
                            on job.Init_Sched_Ent_Id equals ent.Id
                            join jobstate in _job_StateRepository.Query()
                            on job.State_Cd equals jobstate.State_Cd
                            join material in _materialRepository.Query()
                            on job.Item_Id equals material.Id
                            select new GettingJobQueueResult
                            {
                                Wo_Id = wo.Wo_Id,
                                Oper_Id = job.Oper_Id,
                                Init_Sched_Ent_Id = job.Init_Sched_Ent_Id,
                                Init_Sched_Ent_Name = ent.ProductionUnitName,
                                State_Cd = job.State_Cd,
                                State_Desc = jobstate.State_Desc,
                                Req_Finish_Time_Local = job.Req_Finish_Time_Local,
                                Job_Priority = job.Job_Priority,
                                Process_Id = wo.Process_Id,
                                MaterialId = job.Item_Id,
                                MaterialName = material.Name,
                                Qty_At_Start = job.Qty_At_Start,
                                Qty_Reqd = job.Qty_Reqd,
                                Display_Seq = job.Display_Seq
                            };
            if (!string.IsNullOrEmpty(input.KeyWord))
            {
                queryable = queryable.Where(x => x.Oper_Id.Contains(input.KeyWord) || x.Init_Sched_Ent_Name.Contains(input.KeyWord));
            }
            var data = await queryable.OrderBy(x => x.Wo_Id).ThenBy(x => x.Display_Seq).ToPagedAsync(input.PagedInput.PageNo, input.PagedInput.PageSize, input.PagedInput.TotalItems);
            var result = _mapper.Map<PagedItemsData<GettingJobQueueResult>>(data);
            return result;
        }
    }
}
